use crate::{
    model::{AppState, Command, Service},
    utils,
};
use std::sync::Mutex;
use strum::IntoEnumIterator;
use tauri::{self, State};
use tauri_plugin_shell::process::CommandEvent;

#[tauri::command]
pub fn start_all_service(
    state: State<'_, Mutex<AppState>>,
    app: tauri::AppHandle,
) -> Result<(), String> {
    let mut state = state.lock().expect("lock failed");

    for v in Command::iter() {
        if state.srv.contains_key(&v) {
            continue;
        }

        if let Ok(service) = start(&v, &app) {
            state.srv.insert(v, service);
        } else {
            return Err("failed to start service".to_string());
        }
    }

    Ok(())
}

#[tauri::command]
pub fn stop_all_service(state: State<'_, Mutex<AppState>>) -> Result<(), String> {
    let mut state = state.lock().expect("lock failed");

    for v in Command::iter() {
        if !state.srv.contains_key(&v) {
            continue;
        }
        let service = state.srv.remove(&v).unwrap();

        service.kill().expect("failed to kill service");
        log::info!("stop service {:?} ", v);
    }
    Ok(())
}
#[tauri::command]
pub fn start_service(
    state: State<'_, Mutex<AppState>>,
    app: tauri::AppHandle,
    cmd: Command,
) -> Result<(), &str> {
    let mut state = state.lock().expect("lock failed");
    if state.srv.contains_key(&cmd) {
        return Ok(());
    }

    if let Ok(service) = start(&cmd, &app) {
        state.srv.insert(cmd, service);
        Ok(())
    } else {
        Err("failed to start service")
    }
}

#[tauri::command]
pub fn stop_service(state: State<'_, Mutex<AppState>>, cmd: Command) -> Result<(), String> {
    let mut state = state.lock().expect("lock failed");
    if !state.srv.contains_key(&cmd) {
        return Ok(());
    }

    let service = state.srv.remove(&cmd).unwrap();

    service.kill().expect("failed to kill service");
    log::info!("stop service {:?} ", cmd);
    Ok(())
}

#[tauri::command]
pub fn info_service(state: State<'_, Mutex<AppState>>) -> Result<Vec<Command>, String> {
    let state = state.lock().expect("lock failed");

    let mut res: Vec<Command> = vec![];

    for (k, _) in state.srv.iter() {
        res.push(k.clone());
    }

    Ok(res)
}

fn start(cmd: &Command, app: &tauri::AppHandle) -> Result<Service, String> {
    let shell = match utils::get_service_shell(cmd, app) {
        Some(shell) => shell,
        None => return Err("failed to get service shell".to_string()),
    };

    let (mut rx, child) = shell.spawn().expect("failed to spawn");
    log::info!("started service {:?} pid:{}", cmd, child.pid());

    let c = cmd.clone();
    tauri::async_runtime::spawn(async move {
        while let Some(event) = rx.recv().await {
            if let CommandEvent::Stdout(line) = event {
                let message = String::from_utf8_lossy(&line);
                log::info!("[{:?}] {}", c, message.trim())
            }
        }
    });

    Ok(Service::new(child))
}
